package com.pai4j.common.util.community;

import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.io.font.FontProgram;
import com.itextpdf.io.font.FontProgramFactory;
import com.itextpdf.io.image.ImageData;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.color.Color;
import com.itextpdf.kernel.events.Event;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.kernel.pdf.xobject.PdfFormXObject;
import com.itextpdf.layout.Canvas;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.BlockElement;
import com.itextpdf.layout.element.IElement;
import com.itextpdf.layout.element.Image;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.font.FontProvider;
import com.itextpdf.layout.property.TextAlignment;
import com.itextpdf.layout.property.VerticalAlignment;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

@Slf4j
public class Html2PdfUtil {

    /**
     * pdf 页眉边距
     */
    public static final float TOP_MARGIN = 0f;
    public static final float BOTTOM_MARGIN = 0f;
    public static final float LEFT_MARGIN = 0f;
    public static final float RIGHT_MARGIN = 0f;

    private static final String FONT_NAME = "/chat4j/fonts/PingFang SC Regular.ttf";

    /**
     * 生成无页眉、无页脚、无水印pdf
     *
     * @param html
     * @return
     * @throws IOException
     */
    public static byte[] convert(String html) throws IOException {

        return convert(html, null, null, null);
    }

    /**
     * 生成有页眉、页脚，无水印的pdf
     * @param html
     * @param header
     * @param footer
     * @return
     * @throws IOException
     */
    public static byte[] convert(String html,  Header header, Footer footer) throws IOException {

        return convert(html, header, footer, null);
    }

    /**
     * 生成只带有水印的PDF
     *
     * @param html
     * @param watermarkingEventHandler
     * @return
     * @throws IOException
     */
    public static byte[] convert(String html, WatermarkingEventHandler watermarkingEventHandler) throws IOException {

        return convert(html, null, null, watermarkingEventHandler);
    }

    /**
     * HTML 转 PDF
     *
     * @param html
     * @param header 页眉
     * @param footer 页脚
     * @param watermarkingEventHandler 水印
     * @return
     * @throws IOException
     */
    public static byte[] convert(String html, Header header, Footer footer,
                                 WatermarkingEventHandler watermarkingEventHandler) throws IOException {

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        PdfWriter writer = new PdfWriter(outputStream);
        PdfDocument pdfDocument = new PdfDocument(writer);
        // 添加页眉
        if (header != null) {
            pdfDocument.addEventHandler(PdfDocumentEvent.START_PAGE, header);
        }
        // 添加页脚
        if (footer != null) {
            pdfDocument.addEventHandler(PdfDocumentEvent.END_PAGE, footer);
        }
        // 添加水印
        if (watermarkingEventHandler != null) {
            pdfDocument.addEventHandler(PdfDocumentEvent.INSERT_PAGE, watermarkingEventHandler);
        }
        try {
            ConverterProperties props = new ConverterProperties();
            FontProvider fp = new FontProvider();
            fp.addStandardPdfFonts();
            FontProgram font = loadFont();
            fp.addFont(font);
            props.setFontProvider(fp);
            List<IElement> iElements = HtmlConverter.convertToElements(html, props);
            Document document = new Document(pdfDocument, PageSize.A4, false);
            document.setMargins(TOP_MARGIN, RIGHT_MARGIN, BOTTOM_MARGIN, LEFT_MARGIN);
            for (IElement iElement : iElements) {
                BlockElement blockElement = (BlockElement) iElement;
                blockElement.setMargins(TOP_MARGIN, RIGHT_MARGIN, BOTTOM_MARGIN, LEFT_MARGIN);
                document.add(blockElement);
            }
            document.close();
            return outputStream.toByteArray();
        } catch (Exception e) {
            throw e;
        } finally {
            outputStream.close();
        }
    }

    /**
     * 页眉
     */
    @Data
    public static class Header implements IEventHandler {
        protected float width = 102f;
        protected float height = 32f;
        protected float x = 42f;
        protected float y = 740f;
        private InputStream logo;

        @Override
        public void handleEvent(Event event) {
            PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
            PdfDocument pdf = docEvent.getDocument();
            PdfPage page = docEvent.getPage();
            Rectangle pageSize = page.getPageSize();
            PdfCanvas pdfCanvas = new PdfCanvas(
                    page.getLastContentStream(), page.getResources(), pdf);
            Canvas canvas = new Canvas(pdfCanvas, pdf, pageSize);
            ImageData image = null;
            try {
                image = ImageDataFactory.create(toByteArray(logo));
            } catch (IOException e) {
                e.printStackTrace();
            }
            Image img = new Image(image);
            // 图片宽高
            img.scaleAbsolute(width, height);
            // 图片坐标 左下角(0,0)
            img.setFixedPosition(x, y);
            canvas.add(img);
        }
    }

    /**
     * 页脚
     */
    @Data
    public static class Footer implements IEventHandler {
        // 相对坐标系
        protected PdfFormXObject placeholder;
        protected float x = 82f;
        protected float y = 50f;
        protected float imageWidth = 6f;
        protected float imageHeight = 78f;
        protected float space = 10f;
        protected InputStream logo;

        public Footer() {
            placeholder =
                    new PdfFormXObject(new Rectangle(0, 0, 500, 78));
        }

        @Override
        public void handleEvent(Event event) {
            PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
            PdfDocument pdf = docEvent.getDocument();
            PdfPage page = docEvent.getPage();
            Rectangle pageSize = page.getPageSize();
            PdfCanvas pdfCanvas = new PdfCanvas(
                    page.getLastContentStream(), page.getResources(), pdf);
            pdfCanvas.addXObject(placeholder, x + space, y);
            Canvas canvas = new Canvas(pdfCanvas, pdf, pageSize);
            ImageData image = null;
            try {
                image = ImageDataFactory.create(toByteArray(logo));
            } catch (IOException e) {
                e.printStackTrace();
            }
            Image img = new Image(image);
            img.scaleAbsolute(imageWidth, imageHeight);
            img.setFixedPosition(x, y);
            canvas.add(img);
            writeInfo(pdf);
            pdfCanvas.release();
        }

        public void writeInfo(PdfDocument pdf) {
            Canvas canvas = new Canvas(placeholder, pdf);
            canvas.setFontSize(7.5f);
        }
    }

    /**
     * 水印
     */
    @Data
    public static class WatermarkingEventHandler implements IEventHandler {
        protected float x = 200f;
        protected float y = 200f;
        protected String waterMarkText = "";
        protected int waterMarkFontSize = 20;
        protected int angle = 20;

        @Override
        public void handleEvent(Event event) {
            PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
            PdfDocument pdfDoc = docEvent.getDocument();
            PdfPage page = docEvent.getPage();
            PdfFont font = null;
            FontProgram fontProgram = loadFont();
            try {
                font = PdfFontFactory.createFont(fontProgram);
            } catch (IOException e) {
                e.printStackTrace();
            }
            PdfCanvas canvas = new PdfCanvas(page.newContentStreamBefore(), page.getResources(), pdfDoc);
            for (int i = 100; i < page.getPageSize().getWidth();) {
                for (int j = 100; j < page.getPageSize().getHeight();) {
                    new Canvas(canvas, pdfDoc, page.getPageSize())
                            .setFontColor(Color.LIGHT_GRAY, 0.4f)
                            .setFontSize(10)
                            .setFont(font)
                            .showTextAligned(new Paragraph(waterMarkText), i, j, pdfDoc.getPageNumber(page),
                                    TextAlignment.CENTER, VerticalAlignment.MIDDLE, angle);
                    j += y;
                }
                i += x;
            }
        }
    }

    public static byte[] toByteArray(InputStream input) throws IOException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        byte[] buffer = new byte[4096];
        int n = 0;
        while (-1 != (n = input.read(buffer))) {
            output.write(buffer, 0, n);
        }
        return output.toByteArray();
    }

    /**
     * load 字体文件
     */
    private static FontProgram loadFont() {
        try {
            InputStream fontInputStream = Html2PdfUtil.class.getResourceAsStream(FONT_NAME);
            byte[] bytes = IOUtils.toByteArray(fontInputStream);
            return FontProgramFactory.createFont(bytes);
        } catch (Exception e) {
            log.error("生成PDF，字体文件加载失败！FONT_NAME:{}", FONT_NAME, e);
        }
        return null;
    }
}